package top.minuy.subject.leetcode._1091;

import java.util.ArrayList;

/**
 * 1091. 二进制矩阵中的最短路径
 * 给你一个 n x n 的二进制矩阵 grid 中，返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径，返回 -1 。
 * <p>
 * 二进制矩阵中的 畅通路径 是一条从 左上角 单元格（即，(0, 0)）到 右下角 单元格（即，(n - 1, n - 1)）的路径，该路径同时满足下述要求：
 * <p>
 * 路径途经的所有单元格都的值都是 0 。
 * 路径中所有相邻的单元格应当在 8 个方向之一 上连通（即，相邻两单元之间彼此不同且共享一条边或者一个角）。
 * 畅通路径的长度 是该路径途经的单元格总数。
 * <p>
 * <p>
 * <p>
 * 示例 1：
 * <p>
 * <p>
 * 输入：grid = [[0,1],[1,0]]
 * 输出：2
 * 示例 2：
 * <p>
 * <p>
 * 输入：grid = [[0,0,0],[1,1,0],[1,1,0]]
 * 输出：4
 * 示例 3：
 * <p>
 * 输入：grid = [[1,0,0],[1,1,0],[1,1,0]]
 * 输出：-1
 * <p>
 * <p>
 * 提示：
 * <p>
 * n == grid.length
 * n == grid[i].length
 * 1 <= n <= 100
 * grid[i][j] 为 0 或 1
 * 通过次数30,304提交次数79,933
 *
 * @author Minuy
 * @time 18:30
 * @date 2021/11/20
 */
public class Solution {
    int n;
    int[][] dirs8 = {
            {-1, -1}, {-1, 0}, {-1, 1},
            {0, -1}, {0, 1},
            {1, -1}, {1, 0}, {1, 1}
    };
    boolean[][] isVisited;
    int[][] dis;
    int[][] grid;

    public int shortestPathBinaryMatrix(int[][] grid) {
        if (grid == null) {
            return -1;
        }
        if (grid.length == 0) {
            return -1;
        }
        if (grid.length != grid[0].length) {
            return -1;
        }

        n = grid.length;
        isVisited = new boolean[n][n];
        dis = new int[n][n];
        this.grid = grid;

        if (n == 1) {
            if (grid[0][0] == 1) {
                return -1;
            } else {
                return 1;
            }
        }

        if (grid[n - 1][n - 1] == 1) {
            return -1;
        }

        bfs();

        if (!isVisited[n - 1][n - 1]) { // 如果最后一个没有被访问到，直接是不联通
            return -1;
        }

        return dis[n - 1][n - 1];
    }

    private void bfs() {
        ArrayList<Integer> queue = new ArrayList<>();
        if (grid[0][0] == 0) {
            int v = 0;
            int x = v / n;
            int y = v % n;
            queue.add(v);
            isVisited[x][y] = true;
            dis[x][y] = 1; // 从1开始，0代表没有处理

            while (!queue.isEmpty()) {
                v = queue.remove(0);
                x = v / n;
                y = v % n;

                // 找出与v相连的顶点，并逐一加入队列
                for (int[] ints : dirs8) {
                    int x1 = x + ints[0];
                    int y1 = y + ints[1];

                    // 判断坐标是否合法
                    if (x1 >= 0 && y1 >= 0 && x1 < n && y1 < n) {
                        // 判断v周边的顶点是否与v相连
                        if (grid[x1][y1] == 0) {
                            if (!isVisited[x1][y1]) {
                                int v1 = x1 * n + y1;
                                queue.add(v1);
                                dis[x1][y1] = dis[x][y] + 1; // 计算距离
                                isVisited[x1][y1] = true;
                            }
                        }
                    }
                }
            }
        }
    }

}
